package com.chunlin.lambda;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

public class LambdaTest {
    //基本语法: (parameters) -> expression 或 (parameters) ->{ statements; }
    //Lambda表达式由三部分组成：
    //1. paramaters：类似方法中的形参列表，这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明
    //也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号
    //2. ->：可理解为“被用于”的意思
    //3. 方法体：可以是表达式也可以代码块，是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回
    // 这里的代码块块等同于方法的方法体。如果是表达式，也可以返回一个值或者什么都不反回
    public static void main(String[] args) {
//        test1();
//        test2();
//        test3();
//        test4();
        test5();
    }

    private static void test5() {
        HashMap<Integer, String> hashMap = new HashMap<>();
        hashMap.put(1, "hello");
        hashMap.put(4, "liukun");
        hashMap.put(3, "hello");
        hashMap.put(2, "world");

        hashMap.forEach(new BiConsumer<Integer, String>() {
            @Override
            public void accept(Integer integer, String s) {
                System.out.println(integer + " : " + s);
            }
        });

        hashMap.forEach((Integer integer, String s) -> {
            System.out.println(integer + " : " + s);
        });

    }

    //sort()方法的演示
    private static void test4() {
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("333");
        arrayList.add("1");
        arrayList.add("4444");
        arrayList.add("22");
        arrayList.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //注意这里比较长度
                return o1.length() - o2.length();
            }
        });
        arrayList.forEach((String s) -> {
            System.out.println(s);
        });

        arrayList.sort((String s1, String s2) -> {
            return s2.length() - s1.length();
        });
        arrayList.forEach((String s) -> {
            System.out.println(s);
        });
    }

    //forEach() 方法演示
    private static void test3() {
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("hello");
        arrayList.add("world");
        arrayList.add("hello");
        arrayList.add("liukun");

        //简单遍历集合中的元素
        //这个forEach函数会调用当前ArrayList中所存在的元素个数的Consumer函数的次数
        arrayList.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s + " ");
            }
        });
        //这个Lambda表达式所对应的类型就是Consumer<T>接口的类型
        int count = 6;
        arrayList.forEach((String s) -> {
            System.out.println(s + ", " + count);
        });

    }

    private static void test2() {
        int a = 100;
        Test test = new Test() {
            @Override
            public void func() {
                //变量a就是，捕获的变量。这个变量要么是被final修饰，如果不是被final修饰的 你要保证在使用之前，没有修改
                //否则代码直接编译报错
                //a = 99;
                System.out.println("我是内部类，且重写了func这个方法！");
                System.out.println("我是捕获到变量 a == " + a
                        + " 我是一个常量，或者是一个没有改变过值的变量！");
            }
        };
        test.func();

        Person person = new Person("John");
        Runnable runnable = () -> {
            System.out.println(person.getName());
            person.setName("Bob"); // 允许修改对象内部状态 //对于对象引用，你不能再让它指向一个新的对象（即不能更改引用本身），但你可以修改该对象引用指向的对象的内部状态（例如调用对象的setter方法）
            System.out.println(a); // 允许访问，但不能修改a的值
            System.out.println(person.getName());
//            ++a; // 错误：不能修改捕获的变量
        };

        runnable.run();
    }

    private static void test1() {
        //// 1. 不需要参数,返回值为 2
        //() -> 2
        //// 2. 接收一个参数(数字类型),返回其2倍的值
        //x -> 2 * x
        //// 3. 接受2个参数(数字),并返回他们的和
        //(x, y) -> x + y
        //// 4. 接收2个int型整数,返回他们的乘积
        //(int x, int y) -> x * y
        //// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
        //(String s) -> System.out.print(s)

        //Lambda可以理解为：Lambda就是匿名内部类的简化，实际上是创建了一个类，实现了接口，重写了接口的方法
        //在Java中，每个Lambda表达式都必须对应一个特定的函数式接口
        // 这个函数式接口定义了一个抽象方法，Lambda表达式的参数类型、数量以及返回类型都必须与这个接口中的抽象方法相匹配
        //没有使用lambda表达式的时候的调用方式 vs 使用lambda表达式

        //语法精简
        //1. 参数类型可以省略，如果需要省略，每个参数的类型都要省略
        //2. 参数的小括号里面只有一个参数，那么小括号可以省略
        //3. 如果方法体当中只有一句代码，那么大括号可以省略
        //4. 如果方法体中只有一条语句，且是return语句，那么大括号可以省略，且去掉return关键字
        NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
            @Override
            public void test() {
                System.out.println("NoParameterNoReturn");
            }
        };
        noParameterNoReturn.test();
        NoParameterNoReturn noParameterNoReturn1 = () -> System.out.println("NoParameterNoReturnLambda");
        noParameterNoReturn1.test();

        OneParameterNoReturn oneParameterNoReturn = new OneParameterNoReturn() {
            @Override
            public void test(int a) {
                System.out.println("OneParameterNoReturn: " + a);
            }
        };
        oneParameterNoReturn.test(1);
        OneParameterNoReturn oneParameterNoReturn1 = a -> System.out.println("OneParameterNoReturnLambda: " + a);
        oneParameterNoReturn1.test(1);

        MoreParameterNoReturn moreParameterNoReturn = new MoreParameterNoReturn() {
            @Override
            public void test(int a, int b) {
                System.out.println("MoreParameterNoReturn: " + a + " " + b);
            }
        };
        moreParameterNoReturn.test(1, 2);
        MoreParameterNoReturn moreParameterNoReturn1 = (int a, int b) -> System.out.println("MoreParameterNoReturnLambda: " + a + " " + b);
        moreParameterNoReturn1.test(1, 2);

        NoParameterReturn noParameterReturn = new NoParameterReturn() {
            @Override
            public int test() {
                return 1;
            }
        };
        System.out.println("NoParameterReturn: " + noParameterReturn.test());
        NoParameterReturn noParameterReturn1 = () -> 2;
        System.out.println("NoParameterReturnLambda: " + noParameterReturn1.test());

        OneParameterReturn oneParameterReturn = new OneParameterReturn() {
            @Override
            public int test(int a) {
                return 1 + a;
            }
        };
        System.out.println("OneParameterReturn: " + oneParameterReturn.test(1));
        OneParameterReturn oneParameterReturn1 = a -> 1 + a;
        System.out.println("OneParameterReturnLambda: " + oneParameterReturn1.test(3));

        MoreParameterReturn moreParameterReturn = new MoreParameterReturn() {
            @Override
            public int test(int a, int b) {
                return a + b;
            }
        };
        System.out.println("MoreParameterReturn: " + moreParameterReturn.test(1, 2));
        MoreParameterReturn moreParameterReturn1 = (int a, int b) -> {
            return a + b;
        };
        System.out.println("MoreParameterReturnLambda: " + moreParameterReturn1.test(3, 4));

        MoreParameterReturn moreParameterReturn2 = new MoreParameterReturn() {
            @Override
            public int test(int a, int b) {
                return a + b;
            }

            //函数式接口是只有一个抽象方法的接口。这并不限制接口可以有多少非抽象方法（已实现的方法），如默认方法或静态方法，以及来自 Object 类的方法
            @Override
            public int hashCode() {
                return 0;
            }
        };
        System.out.println(moreParameterReturn2.hashCode());

    }
}
